﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing.Drawing2D;
using System.Drawing;

namespace Noris.Tools.TraceVisualiser.BlockingVisualiser.Components
{
    #region Ellipse
    /// <summary>
    /// Elipsa.
    /// Je postavena na třídě Area, má proto všechny její dobré vlastnosti.
    /// Vrací elipsu v property GraphicsPath Path.
    /// </summary>
    internal class Ellipse : Area
    {
        #region Konstruktory a implicitní konvertory je nutno všechny zkopírovat
		public Ellipse() : base() { }
		public Ellipse(float x, float y, float width, float height)
            : base(x, y, width, height)
        { }
        public Ellipse(PointF location, SizeF size)
            : base(location, size)
        { }
        public Ellipse(RectangleF rectangle)
            : base(rectangle)
        { }
        public Ellipse(PointF point1, PointF point2)
            : base(point1, point2)
        { }
        public static implicit operator RectangleF(Ellipse value) { return value.Bounds; }
        public static implicit operator Ellipse(RectangleF value) { return new Ellipse(value); }
        #endregion
        #region GraphicsPath
        /// <summary>
        /// Vrátí GraphicsPath.
        /// Třída Ellipse vrací cestu obsahující svoji elipsu.
        /// </summary>
        public override GraphicsPath Path
        {
            get
            {
                GraphicsPath path = new GraphicsPath();
                path.AddEllipse(this.Bounds);
                return path;
            }
        }
        #endregion
    }
    #endregion
    #region BeveledRectagle
    /// <summary>
    /// Zkosený hranatý obdélník.
    /// Je postaven na třídě Area, má proto všechny její dobré vlastnosti.
    /// Vrací tvar v property GraphicsPath Path.
    /// Implicitní zkosení je 10 pixelů.
    /// </summary>
    internal class BeveledRectagle : Area
    {
        #region Konstruktory a implicitní konvertory je nutno všechny zkopírovat
		public BeveledRectagle() : base() { }
        public BeveledRectagle(float x, float y, float width, float height)
            : base(x, y, width, height)
        {
            this._BevelX = 10f;
            this._BevelY = 10f;
        }
        public BeveledRectagle(PointF location, SizeF size)
            : base(location, size)
        {
            this._BevelX = 10f;
            this._BevelY = 10f;
        }
        public BeveledRectagle(RectangleF rectangle)
            : base(rectangle)
        {
            this._BevelX = 10f;
            this._BevelY = 10f;
        }
        public BeveledRectagle(PointF point1, PointF point2)
            : base(point1, point2)
        {
            this._BevelX = 10f;
            this._BevelY = 10f;
        }
        public BeveledRectagle(float x, float y, float width, float height, float bevelX, float bevelY)
            : base(x, y, width, height)
        {
            this._BevelX = bevelX;
            this._BevelY = bevelY;
        }
        public BeveledRectagle(PointF location, SizeF size, float bevelX, float bevelY)
            : base(location, size)
        {
            this._BevelX = bevelX;
            this._BevelY = bevelY;
        }
        public BeveledRectagle(RectangleF rectangle, float bevelX, float bevelY)
            : base(rectangle)
        {
            this._BevelX = bevelX;
            this._BevelY = bevelY;
        }
        public BeveledRectagle(PointF point1, PointF point2, float bevelX, float bevelY)
            : base(point1, point2)
        {
            this._BevelX = bevelX;
            this._BevelY = bevelY;
        }
        public static implicit operator RectangleF(BeveledRectagle value) { return value.Bounds; }
        public static implicit operator BeveledRectagle(RectangleF value) { return new BeveledRectagle(value); }
        #endregion
        #region Bevel property - určují míru zkosení
        /// <summary>
        /// Míra zkosení ve směru X = velikost rohu ve vodorovném směru, počet pixelů.
        /// Zadat lze libovolnou velikost, ale smysluplná hodnota je pouze v rozsahu 0 až Width/2.
        /// </summary>
        public float BevelX
        {
            get
            {
                float max = this.Width / 2f;
                float bev = this._BevelX;
                if (bev < 0f) return 0f;
                if (bev > max) return max;
                return bev;
            }
            set { this._BevelX = value; }
        }
        /// <summary>
        /// Míra zkosení ve směru X = velikost rohu ve vodorovném směru, počet pixelů.
        /// Zadat lze libovolnou velikost, ale smysluplná hodnota je pouze v rozsahu 0 až Width/2.
        /// </summary>
        public float BevelY
        {
            get
            {
                float max = this.Height / 2f;
                float bev = this._BevelY;
                if (bev < 0f) return 0f;
                if (bev > max) return max;
                return bev;
            }
            set { this._BevelX = value; }
        }
        /// <summary>
        /// Příznak, že tento objekt obsahuje reálné zkosení (hodnoty BevelX a BevelY jsou kladné).
        /// Pokud je IsBeveled == false, pak se objekt jeví jako běžný Rectangle.
        /// </summary>
        public bool IsBeveled
        {
            get { return (this.BevelX > 0f && this.BevelY > 0f); }
        }
        private float _BevelX;
        private float _BevelY;
        #endregion
        #region Property BevelPoints : body, kde se láme zkosení hran (8 bodů: 2 u každého rohu)
        /// <summary>
        /// Bod 0 na zkosené hraně: u bodu TopLeft, pod ním.
        /// Ostatní body následují proti směru hodinových ručiček.
        /// </summary>
        public PointF BevelPoint0
        {
            get
            {
                PointF point = this.PointTopLeft;
                if (this.IsBeveled)
                    point.Y += this.BevelY;
                return point;
            }
        }
        /// <summary>
        /// Bod 1 na zkosené hraně: u bodu BottomLeft, nad ním.
        /// Ostatní body následují proti směru hodinových ručiček.
        /// </summary>
        public PointF BevelPoint1
        {
            get
            {
                PointF point = this.PointBottomLeft;
                if (this.IsBeveled)
                    point.Y -= this.BevelY;
                return point;
            }
        }
        /// <summary>
        /// Bod 2 na zkosené hraně: u bodu BottomLeft, vpravo od něj.
        /// Ostatní body následují proti směru hodinových ručiček.
        /// </summary>
        public PointF BevelPoint2
        {
            get
            {
                PointF point = this.PointBottomLeft;
                if (this.IsBeveled)
                    point.X += this.BevelX;
                return point;
            }
        }
        /// <summary>
        /// Bod 3 na zkosené hraně: u bodu BottomRight, vlevo od něj.
        /// Ostatní body následují proti směru hodinových ručiček.
        /// </summary>
        public PointF BevelPoint3
        {
            get
            {
                PointF point = this.PointBottomRight;
                if (this.IsBeveled)
                    point.X -= this.BevelX;
                return point;
            }
        }
        /// <summary>
        /// Bod 4 na zkosené hraně: u bodu BottomRight, nad ním.
        /// Ostatní body následují proti směru hodinových ručiček.
        /// </summary>
        public PointF BevelPoint4
        {
            get
            {
                PointF point = this.PointBottomRight;
                if (this.IsBeveled)
                    point.Y -= this.BevelY;
                return point;
            }
        }
        /// <summary>
        /// Bod 5 na zkosené hraně: u bodu TopRight, pod ním.
        /// Ostatní body následují proti směru hodinových ručiček.
        /// </summary>
        public PointF BevelPoint5
        {
            get
            {
                PointF point = this.PointTopRight;
                if (this.IsBeveled)
                    point.Y += this.BevelY;
                return point;
            }
        }
        /// <summary>
        /// Bod 6 na zkosené hraně: u bodu TopRight, vlevo od něj.
        /// Ostatní body následují proti směru hodinových ručiček.
        /// </summary>
        public PointF BevelPoint6
        {
            get
            {
                PointF point = this.PointTopRight;
                if (this.IsBeveled)
                    point.X -= this.BevelX;
                return point;
            }
        }
        /// <summary>
        /// Bod 7 na zkosené hraně: u bodu TopLeft, vpravo od něj.
        /// Ostatní body následují proti směru hodinových ručiček.
        /// </summary>
        public PointF BevelPoint7
        {
            get
            {
                PointF point = this.PointTopLeft;
                if (this.IsBeveled)
                    point.X += this.BevelX;
                return point;
            }
        }
        #endregion
        #region GraphicsPath
        /// <summary>
        /// Vrátí GraphicsPath.
        /// Třída Ellipse vrací cestu obsahující svoji elipsu.
        /// </summary>
        public override GraphicsPath Path
        {
            get
            {
                if (!IsBeveled) return base.Path;

                GraphicsPath path = new GraphicsPath();
                path.AddLines(new PointF[] { this.BevelPoint0, this.BevelPoint1, this.BevelPoint2, this.BevelPoint3, this.BevelPoint4, this.BevelPoint5, this.BevelPoint6, this.BevelPoint7, this.BevelPoint0 });
                path.CloseFigure();
                return path;
            }
        }
        #endregion
    }
    #endregion
    #region RoundEdgeRectangle
    /// <summary>
    /// Obdélník s kulatými hranami.
    /// Je postaven na třídě BeveledRectagle, má proto všechny její dobré vlastnosti.
    /// Vrací tvar v property GraphicsPath Path.
    /// Implicitní zkosení je 10 pixelů.
    /// </summary>
    internal class RoundEdgeRectangle : BeveledRectagle
    {
        #region Konstruktory a implicitní konvertory je nutno všechny zkopírovat
		public RoundEdgeRectangle() : base() { }
        public RoundEdgeRectangle(float x, float y, float width, float height)
            : base(x, y, width, height)
        { }
        public RoundEdgeRectangle(PointF location, SizeF size)
            : base(location, size)
        { }
        public RoundEdgeRectangle(RectangleF rectangle)
            : base(rectangle)
        { }
        public RoundEdgeRectangle(PointF point1, PointF point2)
            : base(point1, point2)
        { }
        public RoundEdgeRectangle(float x, float y, float width, float height, float bevelX, float bevelY)
            : base(x, y, width, height, bevelX, bevelY)
        { }
        public RoundEdgeRectangle(PointF location, SizeF size, float bevelX, float bevelY)
            : base(location, size, bevelX, bevelY)
        { }
        public RoundEdgeRectangle(RectangleF rectangle, float bevelX, float bevelY)
            : base(rectangle, bevelX, bevelY)
        { }
        public RoundEdgeRectangle(PointF point1, PointF point2, float bevelX, float bevelY)
            : base(point1, point2, bevelX, bevelY)
        { }
        public static implicit operator RectangleF(RoundEdgeRectangle value) { return value.Bounds; }
        public static implicit operator RoundEdgeRectangle(RectangleF value) { return new RoundEdgeRectangle(value); }
        #endregion
        #region Vlastnosti tvaru
        /// <summary>
        /// Příznak kreslení rohů: false = části elipsy, true = bezierova křivka
        /// </summary>
        public bool BezierArcs { get; set; }
		/// <summary>
		/// Síla bezierovy křivky (0 = spojnice v rohu je rovná čára, 1 = spojnice je téměř roh, větší hodnoty než 1 = spojnice začne tvořit smyčku)
		/// </summary>
		public float BezierRadius { get; set; }
		#endregion
        #region Corners: RectangleF v rozích - pro vykreslení oblouků
        /// <summary>
        /// Prostor oblouku 0 (TopLeft)
        /// </summary>
        public RectangleF Corner0
        {
            get
            {
                float w = 2f * this.BevelX;
                float h = 2f * this.BevelY;
                return new RectangleF(this.Left, this.Top, w, h);
            }
        }
        /// <summary>
        /// Prostor oblouku 1 (BottomLeft)
        /// </summary>
        public RectangleF Corner1
        {
            get
            {
                float w = 2f * this.BevelX;
                float h = 2f * this.BevelY;
                return new RectangleF(this.Left, this.Bottom - h, w, h);
            }
        }
        /// <summary>
        /// Prostor oblouku 2 (BottomRight)
        /// </summary>
        public RectangleF Corner2
        {
            get
            {
                float w = 2f * this.BevelX;
                float h = 2f * this.BevelY;
                return new RectangleF(this.Right - w, this.Bottom - h, w, h);
            }
        }
        /// <summary>
        /// Prostor oblouku 3 (TopRight)
        /// </summary>
        public RectangleF Corner3
        {
            get
            {
                float w = 2f * this.BevelX;
                float h = 2f * this.BevelY;
                return new RectangleF(this.Right - w, this.Top, w, h);
            }
        }
        #endregion
        #region GraphicsPath
        /// <summary>
        /// Vrátí GraphicsPath.
        /// Třída Ellipse vrací cestu obsahující svoji elipsu.
        /// </summary>
        public override GraphicsPath Path
        {
            get
            {
                if (!IsBeveled) return base.Path;

                GraphicsPath path = new GraphicsPath();

                if (!this.BezierArcs)
                {   // Části kruhu, z důvodu kreslení kruhu ve směru hodinových ručiček jej generuji v pořadí 7 - 6 ... :
                    path.AddLine(this.BevelPoint7, this.BevelPoint6);
                    path.AddArc(this.Corner3, 270f, 90f);
                    path.AddLine(this.BevelPoint5, this.BevelPoint4);
                    path.AddArc(this.Corner2, 0f, 90f);
                    path.AddLine(this.BevelPoint3, this.BevelPoint2);
                    path.AddArc(this.Corner1, 90f, 90f);
                    path.AddLine(this.BevelPoint1, this.BevelPoint0);
                    path.AddArc(this.Corner0, 180f, 90f);
                }
                else
                {   // Bezierova křivka:
                    path.AddLine(this.BevelPoint0, this.BevelPoint1);
                    path.AddBeziers(new PointF[] { this.BevelPoint1, this.PointBottomLeft, this.PointBottomLeft, this.BevelPoint2 });
                    path.AddLine(this.BevelPoint2, this.BevelPoint3);
                    path.AddBeziers(new PointF[] { this.BevelPoint3, this.PointBottomRight, this.PointBottomRight, this.BevelPoint4 });
                    path.AddLine(this.BevelPoint4, this.BevelPoint5);
                    path.AddBeziers(new PointF[] { this.BevelPoint5, this.PointTopRight, this.PointTopRight, this.BevelPoint6 });
                    path.AddLine(this.BevelPoint6, this.BevelPoint7);
                    path.AddBeziers(new PointF[] { this.BevelPoint7, this.PointTopLeft, this.PointTopLeft, this.BevelPoint0 });
                }
                path.CloseFigure();
                return path;
            }
        }
        #endregion
    }
    #endregion
    #region Arrow
    /// <summary>
    /// Šipka.
    /// Je postavena na třídě Area, má proto všechny její dobré vlastnosti.
    /// Vrací tvar v property GraphicsPath Path.
    /// </summary>
    internal class ArrowXY : Area
    {
        #region Konstruktory a implicitní konvertory je nutno všechny zkopírovat
		public ArrowXY() : base() { }
		public ArrowXY(float x, float y, float width, float height)
            : base(x, y, width, height)
        { this._SetValues(ShapeDirection.Right, 0.75f, 0.85f); }
        public ArrowXY(PointF location, SizeF size)
            : base(location, size)
        { this._SetValues(ShapeDirection.Right, 0.75f, 0.85f); }
        public ArrowXY(RectangleF rectangle)
            : base(rectangle)
        { this._SetValues(ShapeDirection.Right, 0.75f, 0.85f); }
        public ArrowXY(PointF point1, PointF point2)
            : base(point1, point2)
        { this._SetValues(ShapeDirection.Right, 0.75f, 0.85f); }

        public ArrowXY(float x, float y, float width, float height, ShapeDirection direction)
            : base(x, y, width, height)
        { this._SetValues(direction, 0.75f, 0.85f); }
        public ArrowXY(PointF location, SizeF size, ShapeDirection direction)
            : base(location, size)
        { this._SetValues(direction, 0.75f, 0.85f); }
        public ArrowXY(RectangleF rectangle, ShapeDirection direction)
            : base(rectangle)
        { this._SetValues(direction, 0.75f, 0.85f); }
        public ArrowXY(PointF point1, PointF point2, ShapeDirection direction)
            : base(point1, point2)
        { this._SetValues(direction, 0.75f, 0.85f); }

        public ArrowXY(float x, float y, float width, float height, ShapeDirection direction, float lineRatio, float arrowRatio)
            : base(x, y, width, height)
        { this._SetValues(direction, lineRatio, arrowRatio); }
        public ArrowXY(PointF location, SizeF size, ShapeDirection direction, float lineRatio, float arrowRatio)
            : base(location, size)
        { this._SetValues(direction, lineRatio, arrowRatio); }
        public ArrowXY(RectangleF rectangle, ShapeDirection direction, float lineRatio, float arrowRatio)
            : base(rectangle)
        { this._SetValues(direction, lineRatio, arrowRatio); }
        public ArrowXY(PointF point1, PointF point2, ShapeDirection direction, float lineRatio, float arrowRatio)
            : base(point1, point2)
        { this._SetValues(direction, lineRatio, arrowRatio); }

        private void _SetValues(ShapeDirection direction, float lineRatio, float arrowRatio)
        {
            this._Direction = direction;
            this._ArrowLineRatio = lineRatio;
            this._ArrowAngleRatio = arrowRatio;
        }
        public static implicit operator RectangleF(ArrowXY value) { return value.Bounds; }
        public static implicit operator ArrowXY(RectangleF value) { return new ArrowXY(value); }
        #endregion
        #region Arrow property - určují směr, poměr šířky čáry šipky a délku šípu
        /// <summary>
        /// Směr šipky, kam ukazuje.
        /// Default = Right.
        /// </summary>
        public ShapeDirection Direction
        {
            get { return this._Direction; }
            set { this._Direction = value; }
        }
        /// <summary>
        /// Šířka linky šipky (obdélníková část), pro vodorovnou šipku je to Height, pro svislou šipku je to Width.
        /// Zadává se jako poměr vůči celkovému rozměru v odpovídající dimenzi.
        /// Default = 0.75. Rozsah je 0 až 1 včetně.
        /// Zadání hodnoty mimo rozsah nezpůsobí chybu, bude se akceptovat mezní hodnota.
        /// </summary>
        public float ArrowLineRatio
        {
            get
            {
                float v = this._ArrowLineRatio;
                if (v < 0f) return 0f;
                if (v > 1f) return 1f;
                return v;
            }
            set { this._ArrowLineRatio = value; }
        }
        /// <summary>
        /// Velikost směrovky šipky (délka její trojúhelníkové části), vyjádřená v poměru k celkové výšce šipky v jejím směru (pro vodorovnou šipku je základem poměru Height, pro svislou šipku je to Width).
        /// Určuje délku šípu (pro vodorovnou šipku).
        /// Default = 0.85. Hodnota má být 0 nebo kladná (bez omezení) (protože jde o poměr ze směru Y, který určuje rozměr do směru X).
        /// Pokud by velikost šipky byla větší než šířka objektu (pro vodorovnou šipku), pak se šipka vykreslí v celé délce bez linky šipky.
        /// Zadání hodnoty mimo rozsah nezpůsobí chybu, bude se akceptovat mezní hodnota.
        /// </summary>
        public float ArrowAngleRatio
        {
            get
            {
                float v = this._ArrowAngleRatio;
                if (v < 0f) return 0f;
                return v;
            }
            set { this._ArrowLineRatio = value; }
        }
        /// <summary>
        /// Aktuální hodnota pro Šířku linky šipky (obdélníková část), pro vodorovnou šipku je to Height, pro svislou šipku je to Width.
        /// Je odvozena od this.ArrowLineRatio a výšky v aktuálním směru.
        /// </summary>
        public float ArrowLineHeight
        {
            get
            {
                float total = 0f;
                switch (this.Direction)
                {
                    case ShapeDirection.Right:
                    case ShapeDirection.Left:
                        total = this.Height;
                        break;
                    case ShapeDirection.Up:
                    case ShapeDirection.Down:
                        total = this.Width;
                        break;
                }
                float real = total * this.ArrowLineRatio;
                if (real < 0f) return 0f;
                if (real > total) return total;
                return real;
            }
        }
        /// <summary>
        /// Aktuální hodnota pro Velikost směrovky šipky (délka její trojúhelníkové části).
        /// Určuje délku šípu.
        /// Je odvozena od this.ArrowAngleRatio a výšky v aktuálním směru.
        /// </summary>
        public float ArrowAngleWidth
        {
            get
            {
                float total = 0f;
                float real = 0f;
                switch (this.Direction)
                {
                    case ShapeDirection.Right:
                    case ShapeDirection.Left:
                        // Vodorovná: hodnota total omezuje maximum šířky šípu = Width objektu, 
                        //            hodnota real vychází z poměru ArrowAngleRatio a Height:
                        total = this.Width;
                        real = this.Height * this.ArrowAngleRatio;
                        break;
                    case ShapeDirection.Up:
                    case ShapeDirection.Down:
                        total = this.Height;
                        real = this.Width * this.ArrowAngleRatio;
                        break;
                }
                if (real < 0f) return 0f;
                if (real > total) return total;
                return real;
            }
        }
        private ShapeDirection _Direction;
        private float _ArrowLineRatio;
        private float _ArrowAngleRatio;
        #endregion
		#region Overrides
		/// <summary>
		/// Provede transformaci objektu this podle daného režimu. 
		/// Střed transformace leží v daném bodě.
		/// </summary>
		/// <param name="mode"></param>
		/// <param name="center"></param>
		public override void Transform(MatrixBasicTransformType mode, PointF center)
		{
			base.Transform(mode, center);
			this.Direction = TransformDirection(mode, this.Direction);
		}
		#endregion
        #region GraphicsPath
        /// <summary>
        /// Vrátí GraphicsPath.
        /// Třída Ellipse vrací cestu obsahující svoji elipsu.
        /// </summary>
        public override GraphicsPath Path
        {
            get
            {
                GraphicsPath path = new GraphicsPath();
                List<PointF> points = CalculatePoints();
                if (points != null)
                    path.AddLines(points.ToArray());
                path.CloseFigure();
                return path;
            }
        }
        /// <summary>
        /// Sestaví jednotlivé body pro šipku, podle jejího směru
        /// </summary>
        /// <returns></returns>
        protected virtual List<PointF> CalculatePoints()
        {
            switch (this.Direction)
            {
                case ShapeDirection.Right:
                    return CalculatePointsRight();
                case ShapeDirection.Left:
                    return CalculatePointsLeft();
                case ShapeDirection.Up:
                    return CalculatePointsUp();
                case ShapeDirection.Down:
                    return CalculatePointsDown();
            }
            return null;
        }
        /// <summary>
        /// Sestaví jednotlivé body pro šipku doprava
        /// </summary>
        /// <returns></returns>
        protected virtual List<PointF> CalculatePointsRight()
        {
            List<PointF> result = new List<PointF>();
            float c = this.CenterY;
            float h = this.ArrowLineHeight / 2f;
            float w = this.ArrowAngleWidth;
            float a = this.Right - w;
            result.Add(new PointF(this.Left, c));
            result.Add(new PointF(this.Left, c + h));
            result.Add(new PointF(a, c + h));
            result.Add(new PointF(a, this.Bottom));
            result.Add(new PointF(this.Right, c));
            result.Add(new PointF(a, this.Top));
            result.Add(new PointF(a, c - h));
            result.Add(new PointF(this.Left, c - h));
            result.Add(new PointF(this.Left, c));
            return result;
        }
        /// <summary>
        /// Sestaví jednotlivé body pro šipku doleva
        /// </summary>
        /// <returns></returns>
        protected virtual List<PointF> CalculatePointsLeft()
        {
            List<PointF> result = new List<PointF>();
            float c = this.CenterY;
            float h = this.ArrowLineHeight / 2f;
            float w = this.ArrowAngleWidth;
            float a = this.Left + w;
            result.Add(new PointF(this.Right, c));
            result.Add(new PointF(this.Right, c + h));
            result.Add(new PointF(a, c + h));
            result.Add(new PointF(a, this.Bottom));
            result.Add(new PointF(this.Left, c));
            result.Add(new PointF(a, this.Top));
            result.Add(new PointF(a, c - h));
            result.Add(new PointF(this.Right, c - h));
            result.Add(new PointF(this.Right, c));
            return result;
        }
        /// <summary>
        /// Sestaví jednotlivé body pro šipku nahoru
        /// </summary>
        /// <returns></returns>
        protected virtual List<PointF> CalculatePointsUp()
        {
            List<PointF> result = new List<PointF>();
            float c = this.CenterX;
            float h = this.ArrowLineHeight / 2f;
            float w = this.ArrowAngleWidth;
            float a = this.Top + w;
            result.Add(new PointF(c, this.Bottom));
            result.Add(new PointF(c + h, this.Bottom));
            result.Add(new PointF(c + h, a)); ;
            result.Add(new PointF(this.Right, a));
            result.Add(new PointF(c, this.Top));
            result.Add(new PointF(this.Left, a));
            result.Add(new PointF(c - h, a));
            result.Add(new PointF(c - h, this.Bottom));
            result.Add(new PointF(c, this.Bottom));
            return result;
        }
        /// <summary>
        /// Sestaví jednotlivé body pro šipku dolů
        /// </summary>
        /// <returns></returns>
        protected virtual List<PointF> CalculatePointsDown()
        {
            List<PointF> result = new List<PointF>();
            float c = this.CenterX;
            float h = this.ArrowLineHeight / 2f;
            float w = this.ArrowAngleWidth;
            float a = this.Bottom - w;
            result.Add(new PointF(c, this.Top));
            result.Add(new PointF(c - h, this.Top));
            result.Add(new PointF(c - h, a)); ;
            result.Add(new PointF(this.Left, a));
            result.Add(new PointF(c, this.Bottom));
            result.Add(new PointF(this.Right, a));
            result.Add(new PointF(c + h, a));
            result.Add(new PointF(c + h, this.Top));
            result.Add(new PointF(c, this.Top));
            return result;
        }
        #endregion
    }
	#endregion
    #region enum ShapeDirection
    /// <summary>
    /// Směr
    /// </summary>
    public enum ShapeDirection
    {
        None = 0,
        /// <summary>
        /// Směr vpravo
        /// </summary>
        Right,
        /// <summary>
        /// Směr vlevo
        /// </summary>
        Left,
        /// <summary>
        /// Směr nahoru
        /// </summary>
        Up,
        /// <summary>
        /// Směr dolů
        /// </summary>
        Down
    }
    #endregion
}
